package chatControler;


import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import messages.*;
import chatModel.*;

/**
 * Classe ChatControler
 * Les actions à éxécuter par rapport à un évènement sont décidées ici
 */
public class ChatControler {

	private boolean connected;
	private Utilisateur localUser;
	private ModelMsgToGUI msgToGui;
	private ModeleMsgToNI msgToNI;
	private ModeleEtatGUI mdlEtatGui;
	private ModeleEtatNI mdlEtatNI;
	private ArrayList<UtilisateurDistant> otherUserList = new ArrayList<UtilisateurDistant>();
        
	/**
	 * Constructeur
         * Initialise l'utilisateur local avec les paramètres à null dans le constructeur
	 */
	public ChatControler() {
		this.localUser = new Utilisateur(null, null);
		this.msgToGui = new ModelMsgToGUI(this.localUser);
		this.msgToNI = new ModeleMsgToNI(this.localUser);
		this.mdlEtatGui = new ModeleEtatGUI();
		this.mdlEtatNI = new ModeleEtatNI();
		otherUserList = new ArrayList();
	}
        
        /**
         * Donne l'utilisateur local
         * @return Utilisateur: l'utilisateur local
         */
	public Utilisateur getLocalUser() {
		return localUser;
	}
        
        /**
         * Permet de changer l'utilisateur local
         * @param localUser Utilisateur local
         */
	public void setLocalUser(Utilisateur localUser) {
		this.localUser = localUser;
	}
        
        /**
         * Permet d'obtenir le(s) message(s) destiné(s) au GUI
         * @see chatModel.ModelMsgToGUI
         * @return ModelMsgToGUI
         */
	public ModelMsgToGUI getMsgToGui() {
		return msgToGui;
	}
        
        /**
         * Permet de changer le(s) message(s) destiné(s) au GUI
         * @see chatModel.ModelMsgToGUI
         * @param msgToGui Message pour le GUI
         */
	public void setMsgToGui(ModelMsgToGUI msgToGui) {
		this.msgToGui = msgToGui;
	}
        
        /**
         * Permet d'obtenir le(s) message(s) destiné(s) au NI
         * @see chatModel.ModeleMsgToNI
         * @return ModeleMsgToNI
         */
	public ModeleMsgToNI getMsgToNI() {
		return msgToNI;
	}
        
        /**
         * Changer le(s) message(s) destiné(s) au NI
         * @see chatModel.ModeleMsgToNI
         * @param msgToNI Message pour le NI
         */
	public void setMsgToNI(ModeleMsgToNI msgToNI) {
		this.msgToNI = msgToNI;
	}
        
        /**
         * Donne le modèle décrivant l'état du GUI
         * @see chatModel.ModeleEtatGUI
         * @return ModeleEtatGUI
         */
	public ModeleEtatGUI getMdlEtatGui() {
		return mdlEtatGui;
	}
        
        /**
         * Change le modèle décrivant l'état du GUI
         * @see chatModel.ModeleEtatGUI
         * @param mdlEtatGui Modèle de l'état du GUI
         */
	public void setMdlEtatGui(ModeleEtatGUI mdlEtatGui) {
		this.mdlEtatGui = mdlEtatGui;
	}
	
        /**
         * Donne le modèle décrivant l'état du NI
         * @see chatModel.ModeleEtatNI
         * @return ModeleEtatNI
         */
	public ModeleEtatNI getMdlEtatNi() {
		return mdlEtatNI;
	}
        
        /**
         * Change le modèle décrivant l'état du NI
         * @see chatModel.ModeleEtatNI
         * @param mdlEtatNI Modèle de l'état du NI
         */
	public void setMdlEtat(ModeleEtatNI mdlEtatNI) {
		this.mdlEtatNI = mdlEtatNI;
	}
        
        /**
         * Donne la liste des utilisateurs distants
         * @see chatModel.UtilisateurDistant
         * @return ArrayList
         */
	public ArrayList getOtherUserList(){
		return this.otherUserList;
	}
        
        /**
         * Indique l'état du chatControler
         * @return true si connecté, flase sinon
         */
	public boolean isConnected() {
		return connected;
	}
        
        /**
         * Change l'état du chatControler
         * En faisant la connection, le GUI change de Fenetre et le NI envoie le hello en broadcast
         * @param connected : true pour le connecter, false pour le déconnecter
         */
	public void setConnected(boolean connected) {
		if (connected==true){
			System.out.println("connect controler");
			this.connected = connected;
			this.msgToGui.getListeUser().addElement(this.otherUserList.get(0).getUserName());
			this.msgToGui.connect();
			this.msgToNI.connect();
			this.mdlEtatGui.connect();
			this.mdlEtatNI.connect();
		}
		if(connected==false){
			this.connected=connected;
			this.msgToGui.disconnect();
			this.msgToNI.disconnect();
			//this.mdlEtat.disconnect();
			this.otherUserList.clear();
		}
	}

	
	/**
         * Indique que le fichier a été envoyé à un utilisateur distant
         * @param user : utilisateur distant
         */
	public void fichierEnvoye(UtilisateurDistant user){
		String msg = "*** Votre fichier a bien été envoyé à " + user.getUserName() + " ! ***";
		this.msgToGui.fichierEnvoyeOuRecu(user, msg);
		this.mdlEtatGui.connect();
		this.mdlEtatNI.connect();
		System.out.println("connecte = " + Choix.etatConnected + " est-ce le meme que le gui : " + mdlEtatGui.getEtat());
	}
	
        /**
         * Indique que le fichier a été reçu d'un utilisateur distant
         * @param user : utilisateur distant
         */
	public void fichierRecu(UtilisateurDistant user){
		this.mdlEtatGui.connect();
		this.mdlEtatNI.connect();
		String msg = "*** Le fichier de " + user.getUserName() + " a bien été reçu ! *** ";
		this.msgToGui.fichierEnvoyeOuRecu(user, msg);
		System.out.println("connecte = " + Choix.etatConnected + " est-ce le meme que le gui : " + mdlEtatGui.getEtat());
	}
	
        /**
         * Envoi un message à un utilisateur distant
         * @param pseudoUserDistant : Nom de l'utilisateur distant
         * @param msg : Message à envoyer
         */
	public void envoyerMsg(String pseudoUserDistant, String msg){
		int i =0;
		boolean trouve = false;
		UtilisateurDistant user=null;
		System.out.println("je rentre dans la méthode du controler");
		while( (i< otherUserList.size()) && (!trouve) ){
			System.out.println("je cherche le type");
			//si l'utilisateur existait on le trouve
			if (otherUserList.get(i).getUserName().compareTo(pseudoUserDistant)==0){
				user = otherUserList.get(i);
				trouve=true;
				this.msgToGui.afficherMsgToSend(user, msg);
				this.msgToNI.sendMsg(user, msg);
			}
			i++;
		}
		System.out.println("j'envoie aux modeles");
	}
        
        /**
         * Envoi une proposition de fichier à un utilisateur distant
         * @param pseudoUserDistant : pseudo de l'utilisateur distant
         * @param file : Fichier à envoyer
         */
	public void envoyerDemandeFile(String pseudoUserDistant, File file){
		int i =0;
		boolean trouve = false;
		UtilisateurDistant user=null;
		System.out.println("je rentre dans la méthode du controler");
		while( (i< otherUserList.size()) && (!trouve) ){
			System.out.println("je cherche le type");
			//si l'utilisateur existait on le trouve
			if (otherUserList.get(i).getUserName().compareTo(pseudoUserDistant)==0){
				user = otherUserList.get(i);
				trouve=true;
				this.msgToNI.sendDemandeFile(user, file, file.length());
			}
			i++;
		}
		System.out.println("j'envoie au model");
	}
        
        /**
         * Accepte le fichier proposé par un utilisateur distant
         * @param UserDistant : Utilisateur distant
         * @param nameFile : Nom du fichier proposé
         * @param file : Fichier qui sera le fichier reçu
         */
	public void acceptFile(UtilisateurDistant UserDistant, String nameFile,File file){
		File fileAccepted = new File(file.getPath()+"/"+nameFile);
		System.out.println("chemin fichier à creer : " + fileAccepted.getPath());
		this.msgToNI.acceptFile(UserDistant, fileAccepted);
		this.mdlEtatNI.receivingFile();
		this.mdlEtatGui.receivingFile();

	}
        
        /**
         * Refuse une proposition de fichier d'un utilisateur distant
         * @param UserDistant : Utilisateur distant qui a proposé le fichier
         * @param nomFichier : Nom du fichier proposé
         */
	public void refuseFile(UtilisateurDistant UserDistant, String nomFichier){
		this.msgToNI.refuseFile(UserDistant, nomFichier);
	}
        
        /**
         * Quitte le ChatSystem et déconnecte l'utilisateur
         */
	public void close(){
		System.out.println("controler quitte");
		this.msgToNI.close();
	}
        
        /**
         * Appelée à chaque nouveau message reçu sur le datagram socket
         * @param recvMessage : Message reçu
         * @throws UnknownHostException : Impossible de trouver l'IP"de l'utilisateur distant à partir de son nom d'hôte
         * @throws MessageCreationException : Impossible de créer un message
         */
	public void traiterNewMsg(Message recvMessage /*,ChatNI interfaceReseau*/) throws UnknownHostException, MessageCreationException {
		UtilisateurDistant distantUser, tempDistantUser;

		// Ici, on regarde quel est le type de message qui arrive, et on fait une action en fonction !
		if((msgToGui.getUtilisateurLocal().getUserName().equals(recvMessage.getUsername()))){
			msgToGui.getUtilisateurLocal().setMyIp(InetAddress.getByName(recvMessage.getHostName()));
			msgToNI.getUtilisateurLocal().setMyIp(InetAddress.getByName(recvMessage.getHostName()));
			System.out.println("j'ai reçu un message de moi, je suis connecté : " + msgToGui.getUtilisateurLocal().getUserName());
		}
		//on vérifie que ce n'est pas nous qui envoyons le message, sinon probleme quand on broadcast
		if ((recvMessage.getHostName().compareTo(msgToGui.getUtilisateurLocal().getMyIp().getHostName()) != 0)
				|| ((recvMessage.getUsername().compareTo(msgToGui.getUtilisateurLocal().getUserName())))!=0) {
			System.out.println("Traitement d'un message reçu");
			//1er cas : un reçoit un Hello d'un autre utilisateur
			//On verifie également que l'utilisateur n'est pas déjà
			tempDistantUser = new UtilisateurDistant(recvMessage.getUsername()+"@"+recvMessage.getHostName(), InetAddress.getByName(recvMessage.getHostName()));
			if (recvMessage instanceof Hello && !(this.otherUserList.contains(tempDistantUser))) {
				System.out.print("On a reçu un Hello ");
				distantUser = tempDistantUser;
				otherUserList.add(distantUser);
				this.msgToGui.getListeUser().addElement(distantUser.getUserName());
				System.out.println("j'ai reçu un hello de : " + tempDistantUser.getUserName());
				//On reçoit un Hello positif = Nouvel utilisateur sur le réseau
				if (((Hello) recvMessage).getWaitAck()) {
					//On envoie un hello négatif a cet utilisateur
					System.out.println("positif");
					//On dit au NI d'envoyer l'ack false, et on met à jour la liste d'utilisateur dans le GUI
					this.msgToNI.notifyChatNISendNegHello(distantUser);
					String msg = "     ***" + distantUser.getUserName() + " s'est connecté ! ***";
					this.msgToGui.afficheConnectDeconnect(distantUser, msg);
				}
			}  
			else {//Cas ou l'utilisateur est déjà connu
				/*Si on reçoit un nouveau message, on verifie si l'utilisateur existe*/
				if (recvMessage instanceof ChatMessage) {
					distantUser = tempDistantUser;
					System.out.println("msg reçu de " + distantUser.getUserName());
					this.msgToGui.notifyGUINewMsg(distantUser, ((ChatMessage) recvMessage).getMsgToSend());


				} else if ((recvMessage instanceof FileNotif) && (this.mdlEtatGui.getEtat()==Choix.etatConnected) &&
						(this.mdlEtatNI.getEtat()==Choix.etatConnected)) {
					/*-------------------------------------
					 * On reçoit une demande de fichier----
	                    -------------------------------------*/
					System.out.println("instance filenotif");
					if (((FileNotif) recvMessage).getRequest() == true) {
						distantUser = tempDistantUser;
						System.out.println("on doit accepter/refuser un fichier");
						this.msgToNI.setTailleFileRecu(((FileNotif) recvMessage).getLengthFile());
						System.out.println("on va recevoir un fichier de : " +((FileNotif) recvMessage).getLengthFile());
						this.msgToGui.notifyGUIFileRequest(distantUser, ((FileNotif) recvMessage).getNameFile());
					}
					/*-------------------------------------
					 * On reçoit un acceptation de fichier-
	                 ---------------------------------------*/
					else if ((((FileNotif) recvMessage).getRequest() == false) && ((((FileNotif) recvMessage).getAccepted() == true))) {
						distantUser = tempDistantUser;	
						System.out.println("il accepte le fichier");
						this.msgToGui.notifyGUISendFile(distantUser, ((FileNotif) recvMessage).getNameFile());
						this.msgToNI.notifyNISendFile(distantUser, ((FileNotif) recvMessage).getNameFile());
						this.mdlEtatNI.sendingFile();
						this.mdlEtatGui.sendingFile();
					}
					else if ((((FileNotif) recvMessage).getRequest() == false) && ((((FileNotif) recvMessage).getAccepted() == false))) {
						distantUser = tempDistantUser;
						System.out.println("il refuse le fichier");
						this.msgToGui.notifyGUIFileRefused(distantUser, ((FileNotif) recvMessage).getNameFile());
					}
					
				} else if (recvMessage instanceof Bye) {
					distantUser = tempDistantUser;
					boolean trouve = false;
					int i = 0;
					while(i<this.otherUserList.size()&& !trouve){
						if(this.otherUserList.get(i).equals(distantUser)){
							trouve=true;
							this.otherUserList.remove(i);
							this.msgToGui.getListeUser().remove(i);

						}
						i++;
					}
					String msg = "     ***" + distantUser.getUserName() + " s'est deconnecté ! ***";
					this.msgToGui.afficheConnectDeconnect(distantUser, msg);
				}
			}
		}
	}

}